// Package queue queue/redis_queue.go
// redis_queue 包使用 Redis List 实现任务队列接口。
package queue

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"px-table-checksum-go/config"
	"px-table-checksum-go/db" // 引入 db 包以使用 ChecksumTask
	"strings"
	"sync" // 引入 sync 包用于 Mutex
	"time"

	"github.com/go-redis/redis/v8"   // 确认导入 redis 包
	log "github.com/sirupsen/logrus" // <-- 导入 logrus
)

// RedisQueue 使用 Redis List 实现的队列。
type RedisQueue struct {
	client   *redis.Client
	ctx      context.Context
	queueKey string
	mu       sync.Mutex // 增加互斥锁保护 closed 状态
	closed   bool
}

// NewRedisQueue 创建一个新的 Redis 队列实例。
func NewRedisQueue(cfg config.RedisConfig) (Queue, error) {
	rdb := redis.NewClient(&redis.Options{
		Addr:     cfg.Address(),
		Password: cfg.Password,
		DB:       cfg.DB,
	})

	ctx := context.Background()
	_, err := rdb.Ping(ctx).Result()
	if err != nil {
		return nil, fmt.Errorf("无法连接到 Redis (%s): %w", cfg.Address(), err)
	}

	queueKey := config.GlobalConfig.Options.QueueName // 使用唯一的队列名
	log.Printf("Redis 队列 %s 初始化成功", queueKey)

	return &RedisQueue{
		client:   rdb,
		ctx:      ctx,
		queueKey: queueKey,
		// closed 默认为 false
	}, nil
}

// Put 将任务序列化为 JSON 并放入 Redis List (RPUSH)。
func (q *RedisQueue) Put(task interface{}) error {
	q.mu.Lock()
	if q.closed {
		q.mu.Unlock()
		return ErrQueueClosed // 如果已关闭，直接返回错误
	}
	q.mu.Unlock() // 提前解锁，避免阻塞序列化

	taskJSON, err := json.Marshal(task)
	if err != nil {
		return fmt.Errorf("序列化任务失败: %w", err)
	}

	// RPush: 将元素添加到列表尾部
	// 需要再次检查 closed 状态，防止在序列化期间队列被关闭
	q.mu.Lock()
	if q.closed {
		q.mu.Unlock()
		return ErrQueueClosed
	}
	err = q.client.RPush(q.ctx, q.queueKey, taskJSON).Err()
	q.mu.Unlock() // 操作完成后解锁

	if err != nil {
		// 检查是否是连接关闭的错误
		if errors.Is(err, redis.ErrClosed) {
			return ErrQueueClosed
		}
		return fmt.Errorf("RPush 到 Redis 队列 %s 失败: %w", q.queueKey, err)
	}
	return nil
}

// Get 从 Redis List 获取任务 (BLPop - 阻塞式左侧弹出)。
func (q *RedisQueue) Get() (interface{}, error) {
	q.mu.Lock()
	if q.closed {
		q.mu.Unlock()
		return nil, ErrQueueClosed
	}
	q.mu.Unlock() // Get 是阻塞操作，提前解锁

	// BLPop: 阻塞式地从列表左侧弹出一个元素，0 表示无限等待
	result, err := q.client.BLPop(q.ctx, 0*time.Second, q.queueKey).Result()

	// 检查 Get 返回后的状态和错误
	q.mu.Lock()
	isClosed := q.closed // 读取关闭状态
	q.mu.Unlock()

	if err != nil {
		// 如果在 Get 返回后队列被关闭了，认为是正常关闭
		if isClosed || errors.Is(err, redis.ErrClosed) {
			return nil, ErrQueueClosed
		}
		// redis.Nil 在 BLPop timeout=0 时不应发生，除非连接异常断开
		if errors.Is(err, redis.Nil) {
			log.Printf("警告: BLPop 从 Redis 队列 %s 收到 redis.Nil，可能连接异常", q.queueKey)
			return nil, fmt.Errorf("BLPop 收到非预期 nil: %w", err) // 返回错误供上层重试
		}
		return nil, fmt.Errorf("BLPop 从 Redis 队列 %s 失败: %w", q.queueKey, err)
	}

	// BLPop 成功返回
	if len(result) != 2 {
		return nil, fmt.Errorf("BLPop 返回结果格式错误: %v", result)
	}

	taskJSON := result[1]
	var task db.ChecksumTask // 反序列化到具体类型
	err = json.Unmarshal([]byte(taskJSON), &task)
	if err != nil {
		log.Printf("警告: 反序列化任务失败，跳过此任务。错误: %v, 数据: %s", err, taskJSON)
		// 返回一个特殊错误或 nil 让调用者知道需要重试？ 还是直接跳过？
		// 暂时返回错误
		return nil, fmt.Errorf("反序列化任务失败: %w", err)
	}

	return &task, nil
}

// Close 关闭队列，不再接受新任务并关闭 Redis 连接。
func (q *RedisQueue) Close() error {
	q.mu.Lock()
	if q.closed {
		q.mu.Unlock()
		return nil // 已经关闭
	}
	log.Printf("关闭 Redis 队列 %s 的连接...", q.queueKey)
	q.closed = true
	q.mu.Unlock() // 解锁后再关闭连接

	// 关闭 Redis 客户端连接
	// Close() 可能会中断正在进行的 BLPop 操作
	return q.client.Close()
}

// Clear 清理 Redis List key。
func (q *RedisQueue) Clear() error {
	q.mu.Lock()
	// 即使队列标记为 closed，也尝试清理 key
	key := q.queueKey
	q.mu.Unlock()

	log.Printf("清理 Redis 队列 %s...", key)
	err := q.client.Del(q.ctx, key).Err()
	if err != nil {
		// **修改:** 如果错误是连接已关闭，则忽略
		if errors.Is(err, redis.ErrClosed) || strings.Contains(err.Error(), "client is closed") {
			log.Printf("信息: 清理 Redis 队列 %s 时连接已关闭，忽略错误。", key)
			return nil // 不是真正的错误，忽略
		}
		// 其他错误（如权限问题）需要报告
		log.Printf("警告: 清理 Redis 队列 %s 失败: %v", key, err)
		return fmt.Errorf("清理 Redis 队列 %s 失败: %w", key, err)
	}
	log.Printf("Redis 队列 %s 已清理或不存在", key)
	return nil
}
